/* 
CDX81 - The Calodox ZX81 Emulator
Copyright (c) 2003 Sebastian Gerlach

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#define DIRECTINPUT_VERSION 0x0800
#define INITGUID
#include <windows.h>
#include <dinput.h>
#include <d3d9.h>
#include "resource.h"
#include "z80.h"

ZX81Emulator emulator;

HWND hWnd;
LPDIRECTINPUT8 lpDI; 
LPDIRECTINPUTDEVICE8 lpKeyboard;
IDirect3D9 *pD3D;
IDirect3DDevice9 *pd3dDevice;

bool realTime = true;
bool framed = false;

#define KEYP(x) (keys[x]&0x80)

void updateKeyState()
{
	static bool fd=false;
 	char keys[256]; 
	HRESULT hr=lpKeyboard->GetDeviceState(256,keys); 
	if(hr==DIERR_INPUTLOST)
	{
		while(hr==DIERR_INPUTLOST)
			hr=lpKeyboard->Acquire(); 
		hr=lpKeyboard->GetDeviceState(256,keys); 
	}
	if(FAILED(hr))
		return;

	if(KEYP(DIK_F1))
	{
		emulator.tapeSignal.load("part1.wav");
		emulator.tapeSignal.origin=emulator.tstates;
	}
	if(KEYP(DIK_F2))
	{
		emulator.tapeSignal.load("part2.wav");
		emulator.tapeSignal.origin=emulator.tstates;
	}
	if(KEYP(DIK_F3))
	{
		emulator.tapeSignal.load("part3.wav");
		emulator.tapeSignal.origin=emulator.tstates;
	}
	if(KEYP(DIK_F4))
	{
		emulator.tapeSignal.load("part4.wav");
		emulator.tapeSignal.origin=emulator.tstates;
	}
	if(KEYP(DIK_F5))
	{
		emulator.tapeSignal.load("part5.wav");
		emulator.tapeSignal.origin=emulator.tstates;
	}
	if(KEYP(DIK_F6))
	{
		if(!fd)
			framed=!framed;
		fd=true;
	}
	else
	{
		fd=false;
	}

	for(int y=0;y<8;y++)
	{
		int b=0;	
		switch(y)
		{
		case 0:	// sft,z,x,c,v
			if(KEYP(DIK_LSHIFT) || KEYP(DIK_RSHIFT) || KEYP(DIK_LCONTROL) || KEYP(DIK_RCONTROL)) b|=1;
			if(KEYP(DIK_Z)) b|=2;
			if(KEYP(DIK_X)) b|=4;
			if(KEYP(DIK_C)) b|=8;
			if(KEYP(DIK_V)) b|=16;
			break;
		case 1:	// a,s,d,f,g 
			if(KEYP(DIK_A)) b|=1;
			if(KEYP(DIK_S)) b|=2;
			if(KEYP(DIK_D)) b|=4;
			if(KEYP(DIK_F)) b|=8;
			if(KEYP(DIK_G)) b|=16;
			break;
		case 2:	// q,w,e,r,t 
			if(KEYP(DIK_Q)) b|=1;
			if(KEYP(DIK_W)) b|=2;
			if(KEYP(DIK_E)) b|=4;
			if(KEYP(DIK_R)) b|=8;
			if(KEYP(DIK_T)) b|=16;
			break;
		case 3:	// 1,2,3,4,5 
			if(KEYP(DIK_1)) b|=1;
			if(KEYP(DIK_2)) b|=2;
			if(KEYP(DIK_3)) b|=4;
			if(KEYP(DIK_4)) b|=8;
			if(KEYP(DIK_5)) b|=16;
			break;

			// right-hand side 
		case 4:	// 0,9,8,7,6 
			if(KEYP(DIK_0)) b|=1;
			if(KEYP(DIK_9)) b|=2;
			if(KEYP(DIK_8)) b|=4;
			if(KEYP(DIK_7)) b|=8;
			if(KEYP(DIK_6)) b|=16;
			break;
		case 5:	// p,o,i,u,y 
			if(KEYP(DIK_P)) b|=1;
			if(KEYP(DIK_O)) b|=2;
			if(KEYP(DIK_I)) b|=4;
			if(KEYP(DIK_U)) b|=8;
			if(KEYP(DIK_Y)) b|=16;
			break;
		case 6:	// ent,l,k,j,h * 
			if(KEYP(DIK_RETURN)) b|=1;
			if(KEYP(DIK_L)) b|=2;
			if(KEYP(DIK_K)) b|=4;
			if(KEYP(DIK_J)) b|=8;
			if(KEYP(DIK_H)) b|=16;
			break;
		case 7:	// spc,dot,m,n,b 
			if(KEYP(DIK_SPACE)) b|=1;
			if(KEYP(DIK_PERIOD)) b|=2;
			if(KEYP(DIK_M)) b|=4;
			if(KEYP(DIK_N)) b|=8;
			if(KEYP(DIK_B)) b|=16;
			break;
		}

		emulator.keyports[y]=((b^31)|0xe0);
	}
}

LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	case WM_ACTIVATE:
		if( WA_INACTIVE != wParam && lpKeyboard )
            lpKeyboard->Acquire();
        break;

	case WM_TIMER:
		// Udate keyboard state
		updateKeyState();
		// Run one ticks worth of Z80 code
		emulator.runOneTick();

		// Copy bitmap
		IDirect3DSurface9 *bb;
		pd3dDevice->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&bb);
		D3DLOCKED_RECT lr;
		bb->LockRect(&lr,NULL,0);

		unsigned char *q=emulator.video;
		static DWORD palette[]={0xffffffff,0xff000000,0xff200020,0xff808080,0xff404040,0xff400040};
		if(!framed)
		{
			palette[3]=0xff000000;
			palette[4]=0xff000000;
			palette[5]=0xff000000;
		}
		else
		{
			palette[3]=0xff808080;
			palette[4]=0xff404040;
			palette[5]=0xff400040;
		}

		for(int j=0;j<ZX_VID_HEIGHT;j++)
		{
			DWORD *p=(DWORD*)(((BYTE*)lr.pBits)+j*lr.Pitch);
			for(int i=0;i<ZX_VID_WIDTH;i++)
				*p++=palette[(*q++)+((i<28 || i>28+302 || j<28 || j>15+273)? 3 : 0)];
		}
		bb->UnlockRect();

		pd3dDevice->Present(0,0,0,0);
		break;
	}
	return DefWindowProc(hWnd,msg,wParam,lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASS wc;
	wc.style=CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc=wndProc;
	wc.cbClsExtra=0;
	wc.cbWndExtra=0;
	wc.hInstance=hInstance;
	wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
	wc.hCursor=LoadCursor(NULL,IDC_ARROW);
	wc.hbrBackground=NULL;
	wc.lpszMenuName=NULL;
	wc.lpszClassName="myClass";
	RegisterClass(&wc);

	hWnd=CreateWindowEx(0,"myClass","Minas presents - CDX81 - the ultimate ZX81 emulator",WS_OVERLAPPEDWINDOW,
							 CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
							 NULL,NULL,hInstance,NULL);
	ShowWindow(hWnd,nCmdShow);
	UpdateWindow(hWnd);

	DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&lpDI, NULL); 
	lpDI->CreateDevice(GUID_SysKeyboard, &lpKeyboard, NULL); 
	lpKeyboard->SetDataFormat(&c_dfDIKeyboard); 
	lpKeyboard->SetCooperativeLevel(hWnd,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); 
	lpKeyboard->Acquire(); 

	pD3D=Direct3DCreate9(D3D_SDK_VERSION);
	D3DPRESENT_PARAMETERS d3dpp; 
	memset(&d3dpp,0,sizeof(d3dpp));
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.BackBufferWidth=414;
	d3dpp.BackBufferHeight=314;
	d3dpp.Flags=D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
	pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp,&pd3dDevice);

	SetTimer(hWnd,1,20,NULL);

	ZX81Emulator::Mode m=ZX81Emulator::ZX81;
	const unsigned char *rom;
	if(m==ZX81Emulator::ZX81)
		rom=(const unsigned char *)LoadResource(hInstance,FindResource(hInstance,(LPCTSTR)IDR_ZX81ROM,"ROM"));
	else
		rom=(const unsigned char *)LoadResource(hInstance,FindResource(hInstance,(LPCTSTR)IDR_ZX80ROM,"ROM"));
	emulator.reset(m,rom,strlen(lpCmdLine) ? lpCmdLine : NULL);

	MSG msg;
	while(true)
	{
		if(realTime || PeekMessage(&msg,NULL,0,0,FALSE))
		{
			if(!GetMessage(&msg,NULL,0,0))
				break;
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		if(!realTime)
			emulator.runOneTick();
	}

	KillTimer(hWnd,1);

	pd3dDevice->Release();
	pD3D->Release();

	lpKeyboard->Unacquire(); 
    lpKeyboard->Release();
	lpDI->Release();

	return (int)msg.wParam;
}
